# Decorators can modify behavior
# convert the result of the decorated func to uppercase letters
import functools


def uppercase(func):
    @functools.wraps(func)
    def wrapper(*args, **kwargs):
        # original_res = func(*args, **kwargs)
        # # 不改变原函数本身，只在装饰时改变函数的行为
        # modified_res = original_res.upper()
        # return modified_res
        return func(*args, **kwargs).upper()
    return wrapper


# multi-decorate
def strong(func):
    @functools.wraps(func)
    def wrapper(*args, **kwargs):
        return '<strong>' + func(*args, **kwargs) + '</strong>'
    return wrapper


def emphasis(func):
    @functools.wraps(func)
    def wrapper(*args, **kwargs):
        return '<em>' + func(*args, **kwargs) + '</em>'
    return wrapper


# the decorators were applied: from bottom to top
@strong
@emphasis
@uppercase
def say_hi_to(name):
    """say hi to someone"""
    return 'Hi, ' + name + '!'


# a trace decorator that logs function arguments and results during execution time:
def trace(func):
    @functools.wraps(func)
    def wrapper(*args, **kwargs):
        print(f'Trace: calling {func.__name__}() '
              f'with {args}, {kwargs}')
        original_res = func(*args, **kwargs)
        print(f'Trace: {func.__name__}() '
              f'returned {original_res}')
        return original_res
    return wrapper


@uppercase
@trace
def say(name, words):
    """tell something to someone"""
    return f'{name}: {words}'


if __name__ == '__main__':
    print(say_hi_to('Wan Dehua'))
    print(say('Jane', 'I love you'))
    # Write "Debuggable" Decorators by add '@functools.wraps(func)' before wraps()
    print(say_hi_to.__name__)
    print(say_hi_to.__doc__)
    print(uppercase(say_hi_to).__name__)
    print(uppercase(say_hi_to).__doc__)